﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SearchServer.Client
{
     
    public class ClientWrapper
    {
        private string _name;
        private QueryType _type;
        private int _page = 1;
        private int _pageSize = 10;
        private readonly Dictionary<string, bool> _orders = new Dictionary<string, bool>();
        private LinkType? _currentLink;
        private QueryNode _currentSection;
        private string _field = "*";
        private readonly List<QueryNode> _nodes = new List<QueryNode>();

        public ClientWrapper() { }
        public ClientWrapper(string name)
        {
            _name = name;
        }

        public static ClientWrapper Create()
        {
            return new ClientWrapper();
        }

        public static ClientWrapper Create(string indexName)
        {
            return new ClientWrapper(indexName);
        }

        public ClientWrapper From(string indexName)
        {
            _name = indexName;
            return this;
        }

        public ClientWrapper Select(string field)
        {
            _type = QueryType.SELECT;
            _field = field;
            return this;
        }

        public ClientWrapper Count()
        {
            _type = QueryType.COUNT;
            return this;
        }

        public ClientWrapper Update()
        {
            _type = QueryType.UPDATE;
            return this;
        }

        public ClientWrapper Delete()
        {
            _type = QueryType.DELETE;
            return this;
        }

        public ClientWrapper Group(string field)
        {
            _field = field;
            _type = QueryType.GROUP;
            return this;
        }

        public ClientWrapper Insert()
        {
            _type = QueryType.INSERT;
            return this;
        }

        public ClientWrapper Page(int page, int pageSize)
        {
            _page = page;
            _pageSize = pageSize;
            return this;
        }

        public ClientWrapper And()
        {
            _currentLink = LinkType.AND;
            return this;
        }

        public ClientWrapper Or()
        {
            _currentLink = LinkType.OR;
            return this;
        }

        public ClientWrapper SectionBegin()
        {
            if (_currentSection != null)
                return this;

            _currentSection = new QueryNode
            {
                Operator = Operator.SECTION,
                Link = GetLastLinkType()
            };

            return this;
        }

        public ClientWrapper SectionEnd()
        {
            if (_currentSection == null)
                return this;

            var sectionNode = DeepCopy(_currentSection);
            _nodes.Add(sectionNode);
            _currentSection = null;
            return this;
        }

        public ClientWrapper Eq(string field, object value)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value))
                return this;

            AddNode(new QueryNode(field, Operator.EQ, GetLastLinkType(), value));
            return this;
        }

        public ClientWrapper Ne(string field, object value)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value))
                return this;

            AddNode(new QueryNode(field, Operator.NE, GetLastLinkType(), value));
            return this;
        }

        public ClientWrapper Gt(string field, object value)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value))
                return this;

            AddNode(new QueryNode(field, Operator.GT, GetLastLinkType(), value));
            return this;
        }

        public ClientWrapper Lt(string field, object value)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value))
                return this;

            AddNode(new QueryNode(field, Operator.LT, GetLastLinkType(), value));
            return this;
        }

        public ClientWrapper Ge(string field, object value)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value))
                return this;

            AddNode(new QueryNode(field, Operator.GE, GetLastLinkType(), value));
            return this;
        }

        public ClientWrapper Le(string field, object value)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value))
                return this;

            AddNode(new QueryNode(field, Operator.LE, GetLastLinkType(), value));
            return this;
        }

        public ClientWrapper Between(string field, object lowValue, object upValue)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(lowValue) || IsNullOrEmpty(upValue))
                return this;

            var node = new QueryNode(field, Operator.BETWEEN, GetLastLinkType(), lowValue);
            node.Values.Add(upValue.ToString());
            AddNode(node);
            return this;
        }

        public ClientWrapper In(string field, List<object> values)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(values))
                return this;

            AddNode(new QueryNode(field, Operator.IN, GetLastLinkType(), values));
            return this;
        }

        public ClientWrapper Like(string field, object value)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value))
                return this;

            AddNode(new QueryNode(field, Operator.LIKE, GetLastLinkType(), value));
            return this;
        }

        public ClientWrapper IsNull(string field)
        {
            AddNode(new QueryNode(field, Operator.ISNULL, GetLastLinkType(), null));
            return this;
        }

        public ClientWrapper IsNotNull(string field)
        {
            AddNode(new QueryNode(field, Operator.ISNOTNULL, GetLastLinkType(), null));
            return this;
        }

        public ClientWrapper NotIn(string field, List<object> values)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(values))
                return this;

            AddNode(new QueryNode(field, Operator.NOTIN, GetLastLinkType(), values));
            return this;
        }
        /**
         * Fuzzy。
         *
         * @param field 字段名
         * @param value 字段值
         * @param fuzzyLength 模糊匹配长度
         * @return 当前QueryWrapper实例，支持链式操作。
         */
        public ClientWrapper Fuzzy(String field, String value, int fuzzyLength)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value)) { return this; }
            List<String> values = new List<String>();
            values.Add(value);
            values.Add( fuzzyLength.ToString());
            AddNode(new QueryNode(field, Operator.Fuzzy, GetLastLinkType(), values));
            return this;
        }
        /**
           * Parser。
           *
           * @param field 字段名
           * @param value 字段值
           * @return 当前QueryWrapper实例，支持链式操作。
       */
        public ClientWrapper Parser(String field, String value)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value)) { return this; }
            this.AddNode(new QueryNode(field, Operator.Parser, GetLastLinkType(), value));
            return this;
        }    
        /**
         * Parser。
         *
         * @param field 字段名
         * @param value 字段值
         * @return 当前QueryWrapper实例，支持链式操作。
         */
        public ClientWrapper Phrase(String field, String value, int slop)
        {
            if (IsNullOrEmpty(field) || IsNullOrEmpty(value)) { return this; }
            List<String> values = new List<String>();
            values.Add(value);
            values.Add(slop.ToString());
            this.AddNode(new QueryNode(field, Operator.Phrase, GetLastLinkType(), values));
            return this;
        }
        public ClientWrapper OrderBy(string orderField, bool isDesc)
        {
            _orders[orderField] = isDesc;
            return this;
        }

        private void AddNode(QueryNode node)
        {
            if (_currentSection != null)
            {
                node.Link = GetLastLinkType();
                _currentSection.Children.Add(node);
            }
            else
            {
                _nodes.Add(node);
            }
        }

        private LinkType GetLastLinkType()
        {
            LinkType linkType = _currentLink ?? LinkType.AND;
            _currentLink = null;
            return linkType;
        }

        private QueryNode DeepCopy(QueryNode source)
        {
            var node = new QueryNode
            {
                Field = source.Field,
                Operator = source.Operator,
                Link = source.Link,
                Values = new List<string>(source.Values)
            };

            foreach (var sourceChild in source.Children)
            {
                var childNode = new QueryNode
                {
                    Field = sourceChild.Field,
                    Operator = sourceChild.Operator,
                    Link = sourceChild.Link,
                    Values = new List<string>(sourceChild.Values)
                };
                node.Children.Add(childNode);
            }

            return node;
        }

        public override string ToString()
        {
            return Build();
        }

        public string Build()
        {
            var sb = new StringBuilder("{");
            if (!IsNullOrEmpty(_name))
                sb.Append($"\"name\":\"{_name}\",");
            if (!IsNullOrEmpty(_type))
                sb.Append($"\"type\":\"{_type}\",");
            if (!IsNullOrEmpty(_field))
                sb.Append($"\"field\":\"{_field}\",");
            if (!IsNullOrEmpty(_page))
                sb.Append($"\"page\":{_page},");
            if (!IsNullOrEmpty(_pageSize))
                sb.Append($"\"pageSize\":{_pageSize},");
            if (_orders.Count > 0)
            {
                sb.Append("\"orders\":[");
                var entries = _orders.Select(e => $"{{\"{e.Key}\":{e.Value}}}");
                sb.Append(string.Join(",", entries));
                sb.Append("],");
            }
            sb.Append("\"nodes\":[");
            var nodesJson = _nodes.Select(n => BuildNodeJson(n));
            sb.Append(string.Join(",", nodesJson));
            sb.Append("]}");
            return sb.ToString();
        }

        private string BuildNodeJson(QueryNode node)
        {
            var sb = new StringBuilder("{");
            sb.Append("\"values\":[");
            for (int i = 0; i < node.Values.Count; i++) {

                sb.Append("\"").Append(node.Values[i]).Append("\"");
                if (i < node.Values.Count - 1) {
                    sb.Append(",");
                } 
            } 
            sb.Append("],");
            if (!IsNullOrEmpty(node.Field))
                sb.Append($"\"field\":\"{node.Field}\",");
            if (!IsNullOrEmpty(node.Operator))
                sb.Append($"\"operator\":\"{node.Operator}\",");
            if (!IsNullOrEmpty(node.Link))
                sb.Append($"\"link\":\"{node.Link}\",");
            sb.Append("\"children\":[");
            var childrenJson = node.Children.Select(c => BuildNodeJson(c));
            sb.Append(string.Join(",", childrenJson));
            sb.Append("]");
            sb.Append("}");
            return sb.ToString();
        }
         

        // QueryNode类定义 
        public class QueryNode
        {
            public List<string> Values { get; set; } = new List<string>();
            public string Field { get; set; }
            public Operator Operator { get; set; }
            public LinkType Link { get; set; }
            public List<QueryNode> Children { get; set; } = new List<QueryNode>();

            public QueryNode() { }
            public QueryNode(string field, Operator op, LinkType link, object value)
            {
                Field = field;
                Operator = op;
                Link = link;
                if (value is List<String>) {
                    foreach (object o in ((List<String>)value)){
                        Values.Add(o.ToString()); 
                    } 
                } 
                else
                    Values.Add(value.ToString()); 
            }
        }

        private bool IsNullOrEmpty(object obj)
        {
            if (obj == null)
                return true;
            else if (obj is string str)
                return string.IsNullOrEmpty(str);
            else if (obj is ICollection collection)
                return collection.Count == 0;
            else if (obj is IDictionary dictionary)
                return dictionary.Count == 0;
            else if (obj is IEnumerable enumerable && !(obj is string))
                return !enumerable.Cast<object>().Any();
            else if (obj is Array array)
                return array.Length == 0;
            // 如果不匹配上述任何类型，则认为非空
            return false;
        }
    }
}